上一回在研究 rom/programs/monitor.lua 的過程中
看到一些大寫的變數 _G, _ENV
心想,該是來面對它們的時候了!
先說在 Lua 5.1 或之前的版本
當我們宣告一個全域變數的時候,會自動被存入 _G
這個全域的 table
a = 1
b = "2"
function c()
print(3)
end
print(_G.a) -- 1 _G["a"] 的語法糖
print(_G.b) -- 2 _G["b"] 的語法糖
print(_G.c) -- function: 0x13dc770
_G["c"]() -- 3
如上述程式碼,存取 _G 裡面的元素時,已經預設是全域,不必再指定 _G
並且可以透過在 _G 存入新的值,來宣告全域變數
a = 1
print(a) -- 等同 print(_G.a) 或 print(_G["a"])
_G["d"] = true
print(d) -- true
而事實上,之前我一直在使用的 Lua 內建函式庫
也都會存入 _G 這個大表中,也因此我才可以直接呼叫 table.insert(), io.read(), os.date() 等等
print(io) -- table: 0x195cff0
print(string) -- table: 0x1956ac0
print(table) -- table: 0x19588c0
print(table.insert) -- function: 0x42b750
print(os) -- table: 0x19598d0
print(os.date) -- function: 0x4272d0
print(print) -- function: 0x4215c0 最常用的 print,也是全域函數
如果想要讓變數環境單純一點,而不是每次都去更動 _G 大表呢?
Lua 5.1 提供了 setfenv 方法,可以改變函數的環境
x = 1
myEnv = {}
setfenv(1, myEnv)
print(x) -- 這邊會出錯,因為全新的環境 myEnv 還沒有任何變數或方法,就連 print() 都沒有 !
可以用繼承來解決,所有原本 _G 的變數與方法都會繼承
x = 1
myEnv = {}
setmetatable(myEnv, { __index = _G })
setfenv(1, myEnv)
print(x)
Lua 5.2 開始,加入了新的 _ENV
環境
a = 1 相當於 _ENV['a'] = 1
但是 _G 還是可以使用,只是 _ENV["_G"] 指向了 _ENV
也就是 _ENV["_G"] = _ENV,這是為了相容 Lua 5.1 或更舊的程式碼
a = 1
b = 2
print(_ENV.a)
print(_G.b) -- 相當於 print(_ENV["_G"].b)
此外 setfenv 函數已經廢棄,要在 Lua 5.2 自訂變數環境
則是需要自行宣告新的 _ENV 如下
a = 1
function myfunc()
local _ENV = { print = print, myvar1 = 2 }
myvar2 = false
print(myvar1) -- 2
print(_ENV.myvar2) -- false
print(_ENV.a) -- nil 已經是新的環境變數,所以看不到外面的變數 a
end
myfunc()
print(_ENV.a) -- 1
print(_ENV.myvar1) -- nil 看不到在函數內自定義的環境變數
對環境變數的實驗與學習先到這裡
覺得已經愈來愈清楚 Lua 的整個環境與架構設計
下次來研究 Lua 的 coroutine 函式庫